﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Baik.Interface.Execution;
using Baik.Interface.Framework;

namespace Baik.Execution
{
    public class String : BuiltInObject, IString
    {
        public IExceptionThrower Thrower { get; private set; }
       
        public String(IInjector inject, IExceptionThrower thrower) : base(inject)
        {
            Thrower = thrower;
            this.Assign("concat",  BuiltInFactory.Function(inject, Concat));
            this.Assign("explode",  BuiltInFactory.Function(inject, Explode));
            this.Assign("equals",  BuiltInFactory.Function(inject, BuiltinEquals));
            this.Assign("length",  BuiltInFactory.Function(inject, GetLength));
        }

        private IObject GetLength(IEnumerable<IObject> arg)
        {
            var rtn = Inject.Instantiate<INumber>();
            rtn.Value = Value.Length;
            return rtn;
        }

        private IObject BuiltinEquals(IEnumerable<IObject> arg)
        {
            foreach (var obj in arg)
            {
                if (!(obj is IString)) {Thrower.Throw("you can only compare strings to strings");}
                if (((IString)obj).Value != Value) return Inject.Instantiate<IFalse>();
            }
            return Inject.Instantiate<ITrue>();
        }

        public  string AsString()
        {
            return Value;
        }
        public string Value { get; set; }
        public IObject Concat(IEnumerable<IObject> prms)
        {
            var enumerable = prms as IObject[] ?? prms.ToArray();
            if(enumerable.Any(p => !(p is IString))) Thrower.Throw(" you can only concatenate strings with strings");
            var sw = new StringWriter();
            sw.Write(Value);
            enumerable.Select(p => ((IString) p).Value).ToList().ForEach(sw.Write);
            var rtn = Inject.Instantiate<IString>();
            rtn.Value = sw.ToString();
            return rtn;
        }
        public IObject Explode(IEnumerable<IObject> prms) 
        {
            var enumerable = prms as IObject[] ?? prms.ToArray();
            if (enumerable.Any(p => !(p is IString))) Thrower.Throw(" you can only explode strings with strings");
            var rtn = Inject.Instantiate<IArray>();
            foreach(var str in Value.Split(enumerable.Select(p => ((IString) p).Value.ToCharArray()[0]).ToArray()))
            {
                var ns = Inject.Instantiate<IString>();
                ns.Value = str;
                rtn.Add(ns);
            }
            return rtn;
        }

         
    }
}